# -*- coding: UTF-8 -*-

# Copyright (c) 2007 Daniele Favara <nomed@dsslive.org>.
#
# This is free software you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version.
#
# This software is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software; see the file COPYING.  If not, write to the Free
# Software Foundation, Inc., 51 Franilin St, Fifth Floor, Boston, MA


from math import ceil

class Paginator(object):

    def __init__(self, items, page=0, max_per_page=10):
        if not page:
            page = 0
        offset = page * max_per_page
        self.page = page
        self.max_per_page = max_per_page

        items, num_items, num_pages = paginate(items, page, max_per_page)

        self.items = items
        self.num_items = num_items
        self.num_pages = num_pages
        self.span = offset, offset + len(items)

    def __iter__(self):
        return iter(self.items)

    def __len__(self):
        return len(self.items)

    def __nonzero__(self):
        return len(self.items) > 0

    def __setitem__(self, idx, value):
        self.items[idx] = value

    def has_more_pages(self):
        return self.num_pages > 1
    has_more_pages = property(has_more_pages)

    def has_next_page(self):
        return self.page + 1 < self.num_pages
    has_next_page = property(has_next_page)

    def has_previous_page(self):
        return self.page > 0
    has_previous_page = property(has_previous_page)

def paginate(items, page=0, max_per_page=10):
    """Simple generic pagination.

    Given an iterable, this function returns:
     * the slice of objects on the requested page,
     * the total number of items, and
     * the total number of pages.

    The `items` parameter can be a list, tuple, or iterator:

    >>> items = range(12)
    >>> items
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    >>> paginate(items)
    ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 12, 2)
    >>> paginate(items, page=1)
    ([10, 11], 12, 2)
    >>> paginate(iter(items))
    ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 12, 2)
    >>> paginate(iter(items), page=1)
    ([10, 11], 12, 2)

    This function also works with generators:

    >>> def generate():
    ...     for idx in range(12):
    ...         yield idx
    >>> paginate(generate())
    ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 12, 2)
    >>> paginate(generate(), page=1)
    ([10, 11], 12, 2)

    The `max_per_page` parameter can be used to set the number of items that
    should be displayed per page:

    >>> items = range(12)
    >>> paginate(items, page=0, max_per_page=6)
    ([0, 1, 2, 3, 4, 5], 12, 2)
    >>> paginate(items, page=1, max_per_page=6)
    ([6, 7, 8, 9, 10, 11], 12, 2)
    """
    if not page:
        page = 0
    start = page * max_per_page
    stop = start + max_per_page

    count = None
    if hasattr(items, '__len__'):
        count = len(items)
        if count:
            assert start < count, 'Page %d out of range' % page

    try: # Try slicing first for better performance
        retval = items[start:stop]
    except TypeError: # Slicing not supported, so iterate through the whole list
        retval = []
        for idx, item in enumerate(items):
            if start <= idx < stop:
                retval.append(item)
            # If we already obtained the total number of items via `len()`,
            # we can break out of the loop as soon as we've got the last item
            # for the requested page
            if count is not None and idx >= stop:
                break
        if count is None:
            count = idx + 1

    return retval, count, int(ceil(float(count) / max_per_page))